package com.gss.stockLedger.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import org.apache.log4j.Logger;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.gss.stockLedger.dao.QueryStockNoDaoSupportDAO;
import com.gss.stockLedger.domain.FIFOReportDTO;
import com.gss.stockLedger.domain.StockTransactionDtls;
import com.gss.stockLedger.form.ReportGenerateForm;

@Service
public class WACHybridReportGenerationService {
	
	Logger logger = Logger.getLogger(getClass());
	/*@Autowired
	QueryStockDAO queryStockDAO;*/
	
	@Autowired
	QueryStockNoDaoSupportDAO queryStockNoDaoSupportDAO;
	
	/*@Autowired
	ExcelCreationService excelCreationService;*/
	
	double consumptionRate = 0.0F;
	double effectiveConsumptionRate = 0.0F;
	
	private static Set<String> Consumption_Transaction_Type = new HashSet<String>();
	public int sheetCounter=0;
	static {
		Consumption_Transaction_Type.add("CONS");
		Consumption_Transaction_Type.add("FOC");
		Consumption_Transaction_Type.add("RTV");
		Consumption_Transaction_Type.add("SCRAP");
		Consumption_Transaction_Type.add("CAPTIVE");
		Consumption_Transaction_Type.add("SALES");
		
	}
	
	public boolean generateWACReport(ReportGenerateForm reportForm, Date date) {
			logger.debug("entered WAC");
			boolean resultOfSucess = false;
			GenerateExcelThread generateExcelThread = null;
			long startTime = System.currentTimeMillis();
		try{
		
			List<FIFOReportDTO> finalReportList;

			List<Object[]> partIdList = queryStockNoDaoSupportDAO.getUniquePartIds(reportForm.getInvType(),reportForm.getLastPartId(),reportForm.getCompanyName());
			
			LinkedList<FIFOReportDTO> transactionQ = null;
			Stack<FIFOReportDTO> consumedStack = null;
			LinkedList<FIFOReportDTO> transactionQForReport = null;
			System.out.println(partIdList.size());
			
			String sheetName = reportForm.getReportCategory() + reportForm.getReportType();				//added by ashish
			Workbook workbook = ExcelCreationService.createExcelFile(sheetName,reportForm.getValuationMethod(),reportForm.getStartDate(),
					reportForm.getEndDate(),date,reportForm.getReportCategory(),reportForm.getReportType(),reportForm.getInvType(), reportForm.getCompanyName());			//added by ashish
			logger.debug("default excel workbook created");			//added by ashish
			
			//create excel thread
			generateExcelThread = new GenerateExcelThread(workbook, reportForm, date);
			generateExcelThread.setRunning(true);
			
			generateExcelThread.start();
			
			
			for (Object[] object : partIdList) {
				
				finalReportList = new ArrayList<FIFOReportDTO>();
			
				List<StockTransactionDtls> stockTransactionDtls= queryStockNoDaoSupportDAO.getStockTransactionsForProduct(reportForm,object[0].toString(),object[1].toString());
				System.out.println("number of stock entries to process: " + stockTransactionDtls.size());
				if(stockTransactionDtls.size() == 0)
					continue;
				//sorted list according to receipt date
			
				transactionQ = new LinkedList<FIFOReportDTO>();
				transactionQForReport = new LinkedList<FIFOReportDTO>();
				consumedStack = new Stack<FIFOReportDTO>();
				
				long activeQuantity=0;
				float activeRate=0;
				double activeValue=0.0;
				float currentRate = 0.0F;
				FIFOReportDTO activeRow ;
			
				
			
				// in the following loop, all receipts are accumulated into a linked list so that they can be later used for consumption following FIFO
			

				for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
					activeRow = new FIFOReportDTO();
					BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
					
					if(activeRow.getTransactionType().equalsIgnoreCase("ZGRIR")) {
						//change code to incorporate multiple grir entries on same day with same assignment number
						if(transactionQ.size()>0&&transactionQ.contains(activeRow))
						{
							int index =transactionQ.indexOf(activeRow);
							FIFOReportDTO fromQueue = transactionQ.get(index);
							fromQueue.setQuantity(fromQueue.getQuantity()+activeRow.getQuantity());
							fromQueue.setValue(fromQueue.getValue() + activeRow.getQuantity()*activeRow.getRate());
		// commented by Mayank
		//							if(fromQueue.getQuantity()<=0)
		//								transactionQ.remove(index);
						}
						else
						{
							FIFOReportDTO temp = new FIFOReportDTO();
							BeanUtils.copyProperties(activeRow, temp);
							temp.setValue((double) (temp.getQuantity() * temp.getRate()));
							transactionQ.add(temp);
						}
					}
				}
			
				// applying all adjustments on respective Purchases (GRIR)
			
				for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
					activeRow = new FIFOReportDTO();
					BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
					
					if(activeRow.getTransactionType().equalsIgnoreCase("CUSTOM") || activeRow.getTransactionType().equalsIgnoreCase("COSTPRICE") || 
							activeRow.getTransactionType().equalsIgnoreCase("RETRO") || activeRow.getTransactionType().equalsIgnoreCase("FOREX")
							||activeRow.getTransactionType().equalsIgnoreCase("INVADJ")) {
						
						for (FIFOReportDTO fifoReportDTO : transactionQ) {
							if(null != activeRow.getAssignmentId() && activeRow.getAssignmentId().equalsIgnoreCase(fifoReportDTO.getAssignmentId())){
								if(activeRow.getTransactionType().equalsIgnoreCase("INVADJ")){
									if(activeRow.getQuantity()!=null)
									fifoReportDTO.setQuantity(fifoReportDTO.getQuantity()+activeRow.getQuantity());
								}
								if(activeRow.getValue()!=null)
									fifoReportDTO.setValue(fifoReportDTO.getValue() + activeRow.getValue());
								
								if(fifoReportDTO.getQuantity() != 0)
									fifoReportDTO.setRate((float) (fifoReportDTO.getValue() / fifoReportDTO.getQuantity()));
								else
									fifoReportDTO.setRate(0.0f);
								
								break;
							}
						}
					}
				}
			
				// above loop results into a transactionQ with all receipts along with their reversals and adjustments
				// running loop on this transactionQ, to remove any entries with zero or negative quantity or value
				
								
				List<FIFOReportDTO> toRemoveList = new ArrayList<FIFOReportDTO>();
				for(FIFOReportDTO receipts : transactionQ){
					if(receipts.getQuantity() <=0 || receipts.getValue() <=0){
						//transactionQ.remove(transactionQ.indexOf(receipts));
						toRemoveList.add(receipts);
					}else {
						receipts.setRate((float) (receipts.getValue() / receipts.getQuantity()));
					}
				}
				if(toRemoveList.size() > 0){
					for(FIFOReportDTO index : toRemoveList)
						transactionQ.remove(index);
				}
			
				// now we have transactionQ with all entries having positive Quantity and positive value and rate populated based on final values
			
				long counter=-1;
				FIFOReportDTO previousRow = null;
				for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
				
					activeRow = new FIFOReportDTO();
					BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
					counter++;
					if(counter==0)
					{
						if(activeRow.getTransactionDate().after(reportForm.getStartDate())){
							finalReportList.add(activeRow);
						}
						
						if(activeRow.getTransactionType().equalsIgnoreCase("ZGRIR"))
						{
							FIFOReportDTO temp = new FIFOReportDTO();
							BeanUtils.copyProperties(activeRow, temp);
							if(transactionQ.size()>0 && transactionQ.contains(temp))
							{
								temp.setRate(transactionQ.getFirst().getRate());
								temp.setValue(transactionQ.getFirst().getValue());
								transactionQForReport.add(temp);
								transactionQ.removeFirst();
							}
							
							 consumptionRate = temp.getRate();
							 if(activeRow.getQuantity()>0)
								 effectiveConsumptionRate = consumptionRate; //effectiveconsumption rate is same as consumption rate as it is first entry 
						}
						else if(activeRow.getTransactionType().equalsIgnoreCase("OPENING")){
							FIFOReportDTO temp = new FIFOReportDTO();
							BeanUtils.copyProperties(activeRow, temp);
							//temp.setRate((float) (temp.getValue() / temp.getQuantity()));
							temp.setValue((double) (temp.getRate()*temp.getQuantity()));
							temp.setAssignmentId("OpeningGRN");
							transactionQForReport.add(temp);
							
							//change to show value as product of quantity and rate
							activeRow.setValue((double) (temp.getRate()*temp.getQuantity()));
							
							consumptionRate = temp.getRate();
							if(activeRow.getQuantity()>0)
								effectiveConsumptionRate = consumptionRate;  //effectiveconsumption rate is same as consumption rate as it is first entry
							//transactionQ.removeFirst();
						}else if(WacHybridReportGenerationThreadedService.Consumption_Transaction_Type.contains(activeRow.getTransactionType())){
							
							FIFOReportDTO consumedStockDTO = new FIFOReportDTO();
							consumedStockDTO.setTransactionType("ZGRIR");
							consumedStockDTO.setAssignmentId("ApplicationGenerated");
							consumedStockDTO.setQuantity(activeRow.getQuantity());
							consumedStockDTO.setValue(0.0d);
							consumedStockDTO.setRate(currentRate);
							consumedStack.push(consumedStockDTO);
							
							activeRow.setQuantity(activeRow.getQuantity() * -1);
							activeRow.setRate(0.0f);
							activeRow.setValue(0.0d);
							consumptionRate = 0.0f;
							effectiveConsumptionRate = consumptionRate;  //effectiveConsumption rate is same as consumption rate as it is first entry bot  = 0
						}
						
						activeRow.setClosingQuantity(activeRow.getQuantity()!=null?activeRow.getQuantity():0);
						activeRow.setClosingRate(activeRow.getRate()!=null?activeRow.getRate():0);
						activeRow.setClosingValue(activeRow.getValue()!=null?activeRow.getValue():0);
						activeQuantity = activeRow.getQuantity()!=null?activeRow.getQuantity():0;
						if(activeRow.getRate()!=null&&activeRow.getQuantity()!=null)
							activeValue = activeRow.getRate()*activeRow.getQuantity();
						else
							activeValue = activeRow.getValue();
		
						continue;
					}
					
					String transactionType = stockTransactionDtls2.getTransactionType();
					
					if(transactionType.equalsIgnoreCase("ZGRIR")) //grir is purchase 
					{
		//				System.out.println("###" + activeRow.getAssignmentId() + " :::: " + effectiveConsumptionRate + " ,, " + activeQuantity + "+++ " + activeRate + "/// " + activeValue);
						FIFOReportDTO temp = new FIFOReportDTO();
						BeanUtils.copyProperties(activeRow, temp);
						if(transactionQ.size()>0 && transactionQ.contains(temp))
						{
							temp.setRate(transactionQ.getFirst().getRate());
							temp.setValue(transactionQ.getFirst().getValue());
							transactionQForReport.add(temp);
							transactionQ.removeFirst();
							consumptionRate = temp.getRate();
						}else
							consumptionRate = activeRow.getValue()/activeRow.getQuantity();
						
						if(activeQuantity < 0 ) 
						{ 
							if( (activeQuantity + activeRow.getQuantity()) >= 0) {
								activeQuantity+=activeRow.getQuantity();
								activeRow.setClosingValue(consumptionRate * activeQuantity);
								activeValue = consumptionRate * activeQuantity;
								activeRow.setClosingRate((float) consumptionRate);
								effectiveConsumptionRate = consumptionRate;
							}else{
								activeQuantity+=activeRow.getQuantity();
								activeRow.setClosingValue(effectiveConsumptionRate*activeQuantity);
								activeRow.setClosingRate((float) effectiveConsumptionRate);
								activeValue = activeRow.getClosingValue();
							}
						}
						else if(activeQuantity >= 0)
						{
							activeValue+=(activeRow.getQuantity()*consumptionRate);
							//activeRow.setClosingValue(activeRow.getValue()!=null?activeValue+=activeRow.getValue():activeValue);
							activeRow.setClosingValue(activeValue);
							activeQuantity+=activeRow.getQuantity();
							activeRow.setClosingRate((float) (activeValue/activeQuantity));
							effectiveConsumptionRate = (float) (activeValue/activeQuantity);
		//					System.out.println("***" + activeRow.getAssignmentId() + " :::: " + effectiveConsumptionRate + " ,, " + activeRow.getQuantity() + "+++ " + activeRow.getRate() + "/// " + activeRow.getValue());
		//					System.out.println("%%%%%" + activeRow.getAssignmentId() + " :::: " + effectiveConsumptionRate + " ,, " + activeQuantity + "+++ " + activeRate + "/// " + activeValue);
						}
						
						activeRow.setClosingQuantity(activeQuantity);
						/*if(activeRow.getClosingQuantity()!=0)
						{
							consumptionRate = activeRow.getClosingValue()/activeRow.getClosingQuantity();
							if(activeRow.getClosingQuantity()>0)
								effectiveConsumptionRate = consumptionRate;	//making effective consumption rate = consumption rate only when consumption rate >0
						}
						else
						{
							consumptionRate = 0;
							//effectiveConsumptionRate = consumptionRate;	//not changing the effective consumption rate
						}*/
						
//						System.out.println(activeRow.getAssignmentId() +  " --- " + consumptionRate);
					}
					else if(transactionType.equalsIgnoreCase("CPA"))
					{
						
					}
					else if(WacHybridReportGenerationThreadedService.Consumption_Transaction_Type.contains(transactionType)){
						
						long consumedQuantity = 0;
						long quantityToConsume = activeRow.getQuantity();
						//double consumedQuantityValue = consumptionRate*quantityToConsume;
						double consumedQuantityValue = effectiveConsumptionRate*quantityToConsume;//calculation value using effective conumption rate instead of consumtion rate
		
		
						activeRow.setClosingQuantity(activeQuantity-=quantityToConsume); 
						
							
						if(quantityToConsume < 0){
							activeRow.setClosingRate((float) effectiveConsumptionRate);
							activeRow.setClosingValue((double) (activeRow.getClosingQuantity() * activeRow.getClosingRate()));
							activeValue = activeRow.getClosingValue();
						}
						else{
							if(activeQuantity > 0){
								activeRow.setClosingValue(activeValue-=consumedQuantityValue);
								activeRow.setClosingRate((float) effectiveConsumptionRate);
							}
							else if(activeQuantity <=0){
								activeRow.setClosingRate((float) effectiveConsumptionRate);
								activeRow.setClosingValue((double) (activeRow.getClosingQuantity() * activeRow.getClosingRate()));
								activeValue = activeRow.getClosingValue();
							}
						}
						
						
						/*else
							activeRow.setClosingRate(0.0f);*/
						
						//activeRow.setRate((float) consumptionRate);
						//not sure about this one need to check
						activeRow.setRate((float) effectiveConsumptionRate);
						
						activeRow.setQuantity(quantityToConsume*-1);
						activeRow.setValue(consumedQuantityValue * -1);
						
						//wacReportList.add(activeRow);
					}
					
					else if(activeRow.getTransactionType().equalsIgnoreCase("CUSTOM") || activeRow.getTransactionType().equalsIgnoreCase("COSTPRICE") || 
							activeRow.getTransactionType().equalsIgnoreCase("RETRO") || activeRow.getTransactionType().equalsIgnoreCase("FOREX")||
							activeRow.getTransactionType().equalsIgnoreCase("INVADJ")) {
						if(activeRow.getTransactionType().equalsIgnoreCase("INVADJ")){
							if(activeRow.getQuantity()!=null)
								activeRow.setClosingQuantity(activeQuantity+activeRow.getQuantity());
							else
								activeRow.setClosingQuantity(activeQuantity);
						}
						else
							activeRow.setClosingQuantity(activeQuantity);
						
						activeRow.setClosingValue(activeValue);
						activeRow.setClosingRate((float) effectiveConsumptionRate);
						
						/*if(activeRow.getValue()!=null)
							activeRow.setClosingValue(activeValue+=activeRow.getValue());
						else
							activeRow.setClosingValue(activeValue);//to be changed
						
						activeRow.setClosingRate((float) (activeValue / activeQuantity));
						
						//change as to pick new rate after adjustments
						consumptionRate = activeRow.getClosingRate();
						if(activeRow.getClosingQuantity()>0)
							effectiveConsumptionRate = consumptionRate;*/
						activeRow.setQuantity(null);
						activeRow.setRate(null);
		//					wacReportList.add(activeRow);
					}
					
					if(activeRow.getTransactionDate().after(reportForm.getStartDate()))
						finalReportList.add(activeRow);
					if(finalReportList.size() == 1 && null != previousRow ){
						finalReportList.add(finalReportList.get(0));
						previousRow.setTransactionType("OPENING");
						previousRow.setQuantity(null);
						previousRow.setRate(null);
						previousRow.setValue(null);
						previousRow.setTransactionDate(reportForm.getStartDate());
						finalReportList.set(0, previousRow);
					}
					previousRow = activeRow;
					
				}
			
				//add data in thread map
				generateExcelThread.partId_data_map.put(object[0].toString() + "####" + object[1].toString(), finalReportList);
		//		System.out.println("size of map: " + generateExcelThread.getPartId_data_map().size());
			
			
				logger.debug("\n");
				
			}
			resultOfSucess = true;
			while(generateExcelThread.getPartId_data_map().size() > 0){
				Thread.sleep(2000l);
				System.out.println("Entries left: " + generateExcelThread.getPartId_data_map().size());
			}
			generateExcelThread.setRunning(false);
			generateExcelThread.join();
			System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%  STILL WRITING  %%%%%%%%%%%%%%%%%%%%%%%%%%");
			
		}
		catch (Throwable e) {
			e.printStackTrace();
		}
		finally{
			if(null != generateExcelThread){
				generateExcelThread.setRunning(false);
			}
			System.out.println("time taken: " + (System.currentTimeMillis() - startTime));
		}
//		logger.debug("resultOfSucess: "+resultOfSucess);
		return resultOfSucess;
	
	}
}